/* -- INCLUDES -------------------------------------------------------------- */
#include <stdio.h>
#include <limits.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include "Launcher.h"
#include "Utils.h"

/* -- PRIVATE VARIABLES ----------------------------------------------------- */
static unsigned int alarm_time;
static unsigned int idx;
static char * executable;
char ** argv, ** envp;

/* -- PRIVATE FUNCTIONS ----------------------------------------------------- */
static void fuzz( char * element )
{
	pid_t child, w;
	int status;
	
	child = fork();
	if( child == -1 ) {
		// logmsg( WARNING, logs, "[%03u] Cannot execute line : (fork) %s", line_number, strerror(errno));
	} else if( child == 0 ) {
		close(fileno(stdin));
		close(fileno(stdout));
		close(fileno(stderr));
		
		alarm( alarm_time );
		argv[idx] = element;
		if( execve( executable, argv, envp ) == -1 ) {
			// logmsg( WARNING, logs, "[%03u] Cannot execute line : (execve) %s", line_number, strerror(errno));
			exit(EXIT_FAILURE);
		}
	} else {
		do {
			w = waitpid( child, &status, WUNTRACED | WCONTINUED );
			if( w == -1 ) {
				// logmsg( WARNING, logs, "[%03u] Cannot get exit status : (waitpid) %s", line_number, strerror(errno));
				break;
			} else if( WIFEXITED(status) ) {
				// logmsg( INFO, logs, "[%03u] Exited with status %d", line_number, WEXITSTATUS(status) );
			} else if( WIFSIGNALED(status)) {
				#ifdef WCOREDUMP
				if( WCOREDUMP(status) ) {
					// logmsg( IMPORTANT, logs, "[%03u] Core dump produced");
				} else {
					// logmsg( INFO, logs, "[%03u] No core dump produced", line_number );
				}
				#endif // WCOREDUMP

				// logmsg( IMPORTANT, global_logs, "%s:%u received signal %s", file, line_number, signal_name(WTERMSIG(status)));
				// logmsg( IMPORTANT, logs, "[%03u] Killed by signal %s", line_number, signal_name(WTERMSIG(status)) );

			} else if( WIFSTOPPED(status)) {
				// logmsg( INFO, logs, "[%03u] Stopped by signal %s", line_number, signal_name(WSTOPSIG(status)) );
			} else if( WIFCONTINUED(status)) {
				// logmsg( INFO, logs, "[%03u] Continued...", line_number );
			}
		} while( !WIFEXITED(status) && !WIFSIGNALED(status) );
		// log smthing
	}
}

static void fuzz_content( char * filename )
{
	FILE * fuzzfile;
	unsigned int line_number, length;
	char line[ LINE_SIZE + 1 ] = {0};

	fuzzfile = fopen( filename, "r" );
	if( fuzzfile == NULL ) {
		// logmsg( ERROR, logs, "Cannot open fuzzing file : (fopen) %s", file, strerror(errno) );
	} else {
		// logmsg( INFO, logs, "Fuzzing file opened\n" );
		line_number = 0;
		while( fgets(line, LINE_SIZE, fuzzfile) != NULL ) {
			line_number++;
			length = strlen(line);
			// if( line[ length - 1 ] != '\n' )
				// logmsg( WARNING, logs, "[%03u] Line may have been truncated (get \"%c\" instead of \\n)", line_number, line[ length - 1 ] );
			line[ length - 1 ] = 0;
			
			fuzz( line );
		}
	}
}

/* -- PUBLIC FUNCTIONS ------------------------------------------------------ */
int Launcher( int msgq, char * prog, char ** arguments, char ** environnement, unsigned int arg_idx, bool_t fuzz_with_content, unsigned int maxtime )
{
	char buffer[ MAX_MSG_SIZE ];
	void (*fuzz_function)(char *);
	char * filename;
	msg_p msg;
	ssize_t n;
	pid_t fuzzer;
	
	executable = prog;
	argv = arguments;
	envp = environnement;
	idx = arg_idx;
	alarm_time = maxtime;
	fuzz_function = fuzz_with_content ? fuzz_content : fuzz;
	
	while( (n = msgrcv(msgq, (void*)&buffer, MAX_MSG_SIZE, 0, 0)) > 0 ) {
		msg = (msg_p)&buffer;
		filename = strdup(msg->text);
		
		if( filename == NULL ) {
			// log fuck
		} else {
			fuzzer = fork();
			switch( fuzzer ) {
				case -1:
					// log : cannot lauch fuzzing
					break;
					
				case 0:
					fuzz_function( filename );
					free( filename );
					exit( EXIT_SUCCESS );

				default:
					// log : fuzzing of xxx
					// do nothing
					break;
			}
			free(filename);
		}
	}

	return n;
}
